package de.distmlp.preprocessing;

import java.util.List;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

import de.distmlp.preprocessing.data.MLDataEntry;
import de.distmlp.preprocessing.nlp.dictionary.CreateTrainingsData;
import de.distmlp.preprocessing.nlp.dictionary.Dictionary;
import de.distmlp.preprocessing.normalisation.Normalizer;
import de.distmlp.preprocessing.parser.IParser;
import de.distmlp.preprocessing.parser.MonkParser;
import de.distmlp.preprocessing.parser.XingParser;
import de.distmlp.preprocessing.parser.XingParser_Denoising;
import de.distmlp.preprocessing.parser.XingParser_Identity;

public class Start {

	private static final String MIN_FREQ_OPTION = "minFreq";
	private static final String NB_OUTPUTS_OPTION = "nbOut";
	private static final String NB_INPUTS_OPTION = "nbIn";
	private static final String NORMALIZE_INPUT_OPTION = "n";
	private static final String DICTIONARY_OUTPUT_OPTION = "d";
	private static final String PARSER_OPTION = "p";
	private static final String OUTPUT_FILE_OPTION = "o";
	private static final String INPUT_FILE_OPTION = "i";
	private static final String NB_TRAININGS_DATA = "nbtrain";

	public static void main(final String[] args) throws Exception {
		final Options options = Start.getOptions();

		if (args.length == 0) {
			final HelpFormatter formatter = new HelpFormatter();
			formatter.printHelp("NLP Parser", options);
			return;
		}

		final CommandLineParser cliParser = new BasicParser();
		try {
			// parse the command line arguments
			final CommandLine line = cliParser.parse(options, args);
			final String inputFile = line.getOptionValue(Start.INPUT_FILE_OPTION);
			final String outputFile = line.getOptionValue(Start.OUTPUT_FILE_OPTION);
			final String parserOption = line.getOptionValue(Start.PARSER_OPTION);
			final boolean normalizeInput = line.hasOption(Start.NORMALIZE_INPUT_OPTION);
			final String dictionaryOutputPath = line.getOptionValue(Start.DICTIONARY_OUTPUT_OPTION);

			int maxNbTrainingsData = Integer.MAX_VALUE;
			if (line.hasOption(Start.NB_TRAININGS_DATA)) {
				final String nbTrain = line.getOptionValue(Start.NB_TRAININGS_DATA);
				maxNbTrainingsData = Integer.parseInt(nbTrain);
			}

			IParser parser = null;
			if (parserOption.toLowerCase().equals("mp")) {
				parser = new MonkParser();
				Start.doProcessing(inputFile, outputFile, parser, true, maxNbTrainingsData);
			} else if (parserOption.toLowerCase().startsWith("xp")) {
				final String minFreq = line.getOptionValue(Start.MIN_FREQ_OPTION);
				final String nbInputs = line.getOptionValue(Start.NB_INPUTS_OPTION);
				final String nbOutputs = line.getOptionValue(Start.NB_OUTPUTS_OPTION);
				final Dictionary d = new Dictionary(inputFile, Integer.parseInt(minFreq), Integer.parseInt(nbInputs));

				if (parserOption.toLowerCase().equals("xp")) {
					parser = new XingParser(d, Integer.parseInt(nbInputs), Integer.parseInt(nbOutputs));
				} else if (parserOption.toLowerCase().equals("xpd")) {
					parser = new XingParser_Denoising(d, Integer.parseInt(nbInputs), Integer.parseInt(nbOutputs));
				} else if (parserOption.toLowerCase().equals("xpi")) {
					parser = new XingParser_Identity(d, Integer.parseInt(nbInputs), Integer.parseInt(nbOutputs));
				}

				if (parser != null) {
					Start.doProcessing(inputFile, outputFile, parser, normalizeInput, maxNbTrainingsData);
				}

				if (dictionaryOutputPath != null) {
					Dictionary.writeDictionary(d, dictionaryOutputPath);
				}
			}

		} catch (final ParseException exp) {
			// oops, something went wrong
			System.err.println("Parsing failed.  Reason: " + exp.getMessage());
		}

	}

	private static void doProcessing(final String inputFile, final String outputFile, final IParser parser, final boolean normalizeInput,
			final int nbTrainingsData) throws Exception {
		List<MLDataEntry> mlData = MLDataProcessor.process(inputFile, parser, nbTrainingsData);
		if (mlData.isEmpty()) {
			System.out.println("Nothing to process!");
			return;
		}
		if (normalizeInput) {
			mlData = Normalizer.normalizeInput(mlData);
		}
		CreateTrainingsData.writeTrainingsData(outputFile, mlData);
	}

	private static Options getOptions() {
		final Options options = new Options();

		OptionBuilder.withArgName("File");
		OptionBuilder.hasArg();
		OptionBuilder.isRequired();
		OptionBuilder.withDescription("Use given file as input for creating the trainings data.");
		final Option inputFile = OptionBuilder.create(Start.INPUT_FILE_OPTION);
		options.addOption(inputFile);

		OptionBuilder.withArgName("Path");
		OptionBuilder.hasArg();
		OptionBuilder.isRequired();
		OptionBuilder.withDescription("Use given path to store the trainings data.");
		final Option outputFile = OptionBuilder.create(Start.OUTPUT_FILE_OPTION);
		options.addOption(outputFile);

		OptionBuilder.withArgName("Parser");
		OptionBuilder.hasArg();
		OptionBuilder.isRequired();
		OptionBuilder.withDescription("Choose a parser. (MP=MonkParser, XPD=Xinparser_Denoising, XPI=XingParser_Identity, XP)");
		final Option parser = OptionBuilder.create(Start.PARSER_OPTION);
		options.addOption(parser);

		OptionBuilder.withArgName("Path");
		OptionBuilder.hasArg();
		OptionBuilder.withDescription("Path to store the dictionary at.");
		final Option dictOutput = OptionBuilder.create(Start.DICTIONARY_OUTPUT_OPTION);
		options.addOption(dictOutput);

		final Option normalizeInput = new Option(Start.NORMALIZE_INPUT_OPTION, "normalize the input");
		options.addOption(normalizeInput);

		OptionBuilder.withArgName("nb");
		OptionBuilder.hasArg();
		OptionBuilder.withDescription("Number of input units (Only necessary for Xing).");
		final Option nbInputs = OptionBuilder.create(Start.NB_INPUTS_OPTION);
		options.addOption(nbInputs);

		OptionBuilder.withArgName("nb");
		OptionBuilder.hasArg();
		OptionBuilder.withDescription("Number of output units (Only necessary for Xing).");
		final Option nbOutputs = OptionBuilder.create(Start.NB_OUTPUTS_OPTION);
		options.addOption(nbOutputs);

		OptionBuilder.withArgName("Frequency");
		OptionBuilder.hasArg();
		OptionBuilder.withDescription("Minimum frequency for terms (Only necessary for Xing).");
		final Option minFreq = OptionBuilder.create(Start.MIN_FREQ_OPTION);
		options.addOption(minFreq);

		OptionBuilder.withArgName("nbTrainingsData");
		OptionBuilder.hasArg();
		OptionBuilder.withDescription("Maximum number of Trainingsdata.");
		final Option nbTrainData = OptionBuilder.create(Start.NB_TRAININGS_DATA);
		options.addOption(nbTrainData);

		return options;
	}

}
